

local Session = require("app.public.network.Session")
local Http = require("app.public.network.HttpNetwork")
local Packet = require("app.utils.Packet")
local GameDB = require("app.public.gamedb.init")
local ZoneDB = require("app.public.gamedb.ZoneDB")
local UserData = require("app.public.dao.init")
local AudioManager = require("app.public.audio.AudioManager")
local EventManager = require("app.public.eventmanager.EventManager")
local VideoRecorder = require("app.public.recorder.VideoRecorder")
local LocalNotificationAPI = require("app.public.localnotification.LocalNotificationAPI")
local mvc = require("app.public.mvc.init")
local UiHandler = require("app.ui.UiHandler")
local Socket = require "socket"
local AvatarFactory = require("app.public.avatar.AvatarFactory")
local ADReport = require("app.utils.ADReport")
local PwrdTrackEvent = require("app.utils.PwrdTrackEvent")

local Crontab = require("app.utils.Crontab")

local Director = cc.Director:getInstance()
local FileUtils = cc.FileUtils:getInstance()
local ActionManager = Director:getActionManager()
local EventDispatcher = Director:getEventDispatcher()
local ActionManagerEx = ccs.ActionManagerEx:getInstance()
local ActionTimelineCache = ccs.ActionTimelineCache:getInstance()
local TargetPlatform = cc.Application:getInstance():getTargetPlatform()
local SpriteFrameCache = cc.SpriteFrameCache:getInstance()
local TextureCache = cc.Director:getInstance():getTextureCache()
local SharedApplication = cc.Application:getInstance()
local UserDefault = cc.UserDefault:getInstance()

local GameApp = class("GameApp", cc.mvc.AppBase)

local string_find = string.find
local string_sub = string.sub
local string_len = string.len
local string_split = string.split
local string_urlencode = string.urlencode
local table_concat = table.concat

function globalTouchEventHandler(widget,eventType)
    local property =  widget:getCallbackName()
    if not property or type(property)~="string" or string_len(property)==0 then
        return
    end

    local eventName = nil
    if eventType==ccui.TouchEventType.began then
        eventName="began"
    elseif eventType==ccui.TouchEventType.ended then
        eventName="ended"
    end

    if not eventName then
        return nil
    end

    local soundEffectId = nil
    if string_find(property, "_") then
        local  splitedStr = string_split(property,"_")
        for _,stringToFind in pairs(splitedStr) do
            local index = string_find(stringToFind,eventName)
            if index then
                local idStr =  string_sub(stringToFind,index+string_len(eventName),string_len(stringToFind))
                soundEffectId = tonumber(idStr)
                break
            end
        end
    else
        local index = string_find(property,eventName)
        if index then
            local idStr =  string_sub(property,index+string_len(eventName),string_len(property))
            soundEffectId = tonumber(idStr)
        end
    end

    if soundEffectId then
        AM.playSoundById(soundEffectId)
    end
end

function globalFrameEventHandler(event)
    local i,j= string_find(event,"@sound_")
    if i and i==1 then
        local soundId = string_sub(event,j+1)
        AM.playSoundById(soundId)
    end
end

function globalIsGameLogin()
    if app and app.session then
        print("app.isLogin", app.session:isLogined())
        return app.session:isLogined()
    else
        print("app.isLogin", false)
        return false
    end
end


-- export global variable
local __g = _G
cc.exports = {}
setmetatable(cc.exports, {
    __newindex = function(_, name, value)
        rawset(__g, name, value)
    end,

    __index = function(_, name)
        return rawget(__g, name)
    end
})

-- disable create unexpected global variable
function cc.disable_global()
    setmetatable(__g, {
        __newindex = function(_, name, value)
            error(string.format("USE \" cc.exports.%s = value \" INSTEAD OF SET GLOBAL VARIABLE", name), 0)
        end
    })
end

function GameApp:ctor()
    GameApp.super.ctor(self)
    
    if cc.Configuration then
        self.configuration = cc.Configuration:getInstance()
        self.language = self.configuration:getValue("LANGUAGE") or "CHS"
    end

    if DEBUG==0 then
        print = function() end
        dump = function() end
    end

    if self:isInnerBuild() then
        -- 内网测试，平台接口清空
        im = nil
        tapm = nil
        GemService = nil
    end

    -- 兼容没有im的情况
    if not im then
        im = {}
        setmetatable(im, {__index = function(tab, key)
                print("im don't have func = ", key)
                rawset(tab, key, function() end)
                return tab.key
            end})
        im.isValid = function() return false end
        im.isLogined = function() return false end
    end

    -- 兼容没有tapm的情况
    if not tapm then
        tapm = {}
        setmetatable(tapm, {__index = function(tab, key)
                print("tapm don't have func = ", key)
                rawset(tab, key, function() end)
                return tab.key
            end})
    end

    -- 兼容没有gsdk的情况
    if not GemService then
        GemService = {}
        setmetatable(GemService, {__index = function(tab, key)
                print("GemService don't have func = ", key)
                rawset(tab, key, function() end)
                return tab.key
            end})
    end

    self._eventManager = EventManager.new()
    EM = self._eventManager
    require("app.event")

    self.crashReportAPI = require("app.public.crashreport.CrashReportAPI")
    self.session = Session.new()
    self.http = Http

--    if WatchBridge then
--        self.watchBridge = WatchBridge:getInstance()
--    end

    GD = GameDB.new()

    -- 服务器传过来的配置，应该已经废弃了
    self._zoneDB = ZoneDB.new()
    ZD = self._zoneDB

    assert(L10N)
    L = L10N.getString

    self._userData = UserData.new()
    UD = self._userData

    self.mvc = mvc
    S = handler(GD,GameDB.getSystemPar)

    self._uiHandler = UiHandler.new()
    UIHandler = self._uiHandler

    self:initChannel()
   

    self.notificationAPI = LocalNotificationAPI.new()

    -- 埋点
    adReport = ADReport.new()
    
    -- 完美SDK埋点
    self.pwrdTrackEvent = PwrdTrackEvent.new()
    
    -- randomseed
    local udidStr = device.getOpenUDID()
    local udidmask = 0
    if udidStr then
        for i=1,string.len(udidStr) do
            udidmask = udidmask + string.byte(udidStr,i)
        end
    end
    math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6))+udidmask)

    -- AudioManager
    AM = AudioManager
    local isMuteSound = self:getValue(Enums.UserDefaultKey.MuteSound,false)
    local isMuteMusic = self:getValue(Enums.UserDefaultKey.MuteMusic,false)
    local soundVolumeMax = self:getValue(Enums.UserDefaultKey.SoundVolumeMax,1.0)
    local musicVolumeMax = self:getValue(Enums.UserDefaultKey.MusicVolumeMax,1.0)
    AM.setSoundMute(isMuteSound)
    AM.setMusicMute(isMuteMusic)
    AM.setSoundVolumeMax(soundVolumeMax)
    AM.setMusicVolumeMax(musicVolumeMax)

    -- videoRecorder
    self.videoRecorder = VideoRecorder.new()
    local isToolbarVisible = self:getValue(Enums.UserDefaultKey.ToolbarVisible,false)
    if self.videoRecorder:isToolbarVisible() ~= isToolbarVisible then
        if isToolbarVisible then
            self.videoRecorder:showToolbar()
        else
            self.videoRecorder:hideToolbar()
        end
    end

    -- GM 密令禁止
    local GM_DISABLE = self.configuration:getValue("GM_DISABLE") or false
    if not GM_DISABLE then
        require "app.Command"
    end
    
    self:initTextureHalfSizeParams()
    self:updateTextureHalfSize()

    self:updateFps()

    self._classDict = {}
    self._instDict = {}
    self._midasPermissionOK = true
end

GameApp.modules = {
    "app.scripts",
    "app.world",
    "app.public",
    "app.login",
    "app.guide",
    "app.newerBattleGuide",
    "app.im",
    "app.team",
    "app.player",
    "app.newBag",
    "app.secfunc",
    "app.skill",
    "app.system",
    "app.cosplay",
    "app.pvp",
    "app.activityDungeons",
    "app.chess",
    "app.theatre",
    "app.mail",
    "app.mount",
    "app.friend",
    "app.worldMap",
    "app.mall",
    "app.crystal",
    "app.reward",
    "app.share",
    "app.setting",
    "app.avatarShop",
    "app.newGuild",
    "app.grayBattle",
    "app.handBook",
    "app.task",
    "app.gameplayPreview",
    "app.jewel",
    "app.auction",
    "app.prestige",
    "app.dynastyBattle",
    "app.coreProp",
    "app.boss",
    "app.daily",
    "app.newRank",
    "app.dailyWorkflow",
    "app.partner",
    "app.crossServer",
    "app.legend",
    "app.superRank",
}

function GameApp:run()
    --检查创建全局变量
    if DISABLE_GLOBAL then
        cc.disable_global()
    end

    local gameScene = require("app.scenes.GameScene").new()
    self.scene = gameScene

    -- 初始化 UIHandler topLayer
    UIHandler:initSceneTopLayer()

    -- Press Esc or Back Button quit game
    local keyboardListener = cc.EventListenerKeyboard:create()
    keyboardListener:registerScriptHandler(function(code,event)
        -- print("keyboardListener ",code,event)
        if code==cc.KeyCode.KEY_BACK then
            local confirmDialog = self._quitAppConfirmDialog
            if not confirmDialog then
                local confirmDialog = display.newConfirmDialog(L("QuitAppConfirm"),
                    function()
                        self._quitAppConfirmDialog = nil
                        app:exit()
                    end,
                    function()
                        self._quitAppConfirmDialog = nil
                    end)
                UIHandler:addPopDialog(confirmDialog, 10000)
                self._quitAppConfirmDialog = confirmDialog
            end
        elseif code >= 26 and code <= 29 then
            EM:notify("onDirectKey", code)
        end
    end,cc.Handler.EVENT_KEYBOARD_PRESSED)
    EventDispatcher:addEventListenerWithSceneGraphPriority(keyboardListener,gameScene)

    -- 添加游戏切换前台／后台的监听
    self:addCustomEventListener("APP_ENTER_FOREGROUND_EVENT", function()
            EM:notify("OnAppEnterForeground")
            cc.Device:setKeepScreenOn(true)

            -- 切换后台时间太长（>12h），切回前台直接重启
            local curTime = self:getTime()
            if self._backTime and ((curTime - self._backTime) > 3600*12) then
                print("backgroud duration", curTime - self._backTime)
                self:restartApp()
            else
                BattleManager:resumeBattle(false)
            end
        end, gameScene)
    self:addCustomEventListener("APP_ENTER_BACKGROUND_EVENT", function()
            EM:notify("OnAppEnterBackground")
            BattleManager:pauseBattle(false)
            self:refreshReloadView(-2)
            self._backTime = self:getTime()

            -- test for desktop
            if DEBUG > 0 and not self:isMobilePlatform() then
                app.session:disconnect(nil, false)
            end
        end, gameScene)
    self:addCustomEventListener("RELOAD_TEXTURE_PERCENT_EVENT", function(event)
            local pg = tonumber(event:getUserStr())
            print("RELOAD_TEXTURE_PERCENT_EVENT", pg)
            self:refreshReloadView(pg)
        end, gameScene)
    self:addCustomEventListener("APP_RESTART_EVENT", function(event)
            print("APP_RESTART_EVENT")
            self:onRestartApp()
        end, gameScene)


    if DEBUG_DISPLAY_STATUS then
        self._debugScheduleHandler = scheduler.newCron(function()
            self:updateDebugStatus()
        end,2, true)
    end

    --手势识别画圆
    if self:isInnerBuild() then
        self._gmTouchPoints = {}
        local touchListener = cc.EventListenerTouchOneByOne:create()
        touchListener:registerScriptHandler(
            function(touch, event)
                self._gmTouchPoints = {cc.p(touch:getLocation())}
                return true
            end,cc.Handler.EVENT_TOUCH_BEGAN)
        touchListener:registerScriptHandler(
            function(touch, event)
                if #self._gmTouchPoints < 100 then
                    table.insert(self._gmTouchPoints,cc.p(touch:getLocation()))
                end
            end,cc.Handler.EVENT_TOUCH_MOVED)
        touchListener:registerScriptHandler(
            function(touch, event)
                if #self._gmTouchPoints > 3 then
                    local ret,score = cc.DollarRecognizer(self._gmTouchPoints)
                    --print("====>DollarRecognizer",ret,score)
                    if ret=="Circle" and score>0.85 then
                        local view = require("app.debuger.DebugPanel").new()
                        Director:getRunningScene():addChild(view,9999)
                    end
                end
            end,cc.Handler.EVENT_TOUCH_ENDED)
        Director:getEventDispatcher():addEventListenerWithSceneGraphPriority(touchListener, gameScene)
    end
end

function GameApp:openLoginView(battleGuideRoleId)
    print("GameApp:openLoginView", battleGuideRoleId)
    -- 清理新手战场景
    if battleGuideRoleId ~= nil then
        self:saveValue( Enums.UserDefaultKey.BattleGuideComp, true )
        self._isBattleGuideComp = true
        app:sendMsg("LoadingController", "leaveBattleGuide")
        GemService:SetEvent(11, true, "success", false, false)
        EM:notify("OnGuideBattleAdEvent", Enums.GuideBattleAdEvent.BattleEnd)
    end
    -- 登陆界面初始化
    local LoginController = app:getClass("LoginController")
    local isEnterRolelist = (type(_RelaunchAppAction)=="number" and _RelaunchAppAction==1)
    self.scene:addChild(LoginController.new(isEnterRolelist, battleGuideRoleId), 1)
end

function GameApp:isBattleGuideComp()
    if not GUIDE_BATTLE_ENABLE then return true end

    if type(self._isBattleGuideComp) == "nil" then
        local isComp = self:getValue(Enums.UserDefaultKey.BattleGuideComp, false)
        self._isBattleGuideComp = isComp

        -- for test
        -- self._isBattleGuideComp = false
    end
    if not self._isBattleGuideComp and app.channelAPI:isReview() then
        return true
    end

    return self._isBattleGuideComp
end

function GameApp:updateDebugStatus()
    local layout = self.scene:getChildByTag(100)
    local label = nil
    if not layout then
        layout = cc.LayerColor:create(cc.c4b(100,100,100,140))
        layout:setAnchorPoint(0,1)
        layout:ignoreAnchorPointForPosition(false)
        layout:setPosition(0,display.height-10)
        layout:setContentSize(cc.size(380,150))
        self.scene:addChild(layout,999)
        layout:setTag(100)

        label = display.newTTFLabel({size = 22,align = cc.TEXT_ALIGNMENT_LEFT,valign=cc.TEXT_ALIGNMENT_LEFT})
        layout:addChild(label)
        self._MemoryStatus = label
        label:setAnchorPoint(0,1)
        label:setPosition(0,150)
        label:setTag(1)
    else
        label = layout:getChildByTag(1)
    end

    if label then
        local textureSize = TextureCache:getCachedTextureSize() /1024
        local fontTextureSize = Director:getFontAtlasTextureSize() / 1024
        local formatStr = string.format("TexSize: %.1f M FontAtlasSize: %.1f M",textureSize,fontTextureSize)
        formatStr = string.format("%s\n LuaVM Memory Used:: %.1f M",formatStr,collectgarbage("count")/1024)
        local pingValue = self.session:getDelayTimeMs()
        formatStr = formatStr.."\n Ping:"..pingValue.." ms"

        local nodeCount = display.getNodeCount(Director:getRunningScene())
        formatStr = formatStr.."\n Nodes:"..nodeCount

        local actionManager = Director:getActionManager()
        local runningActionNum = 0
        if actionManager.getNumberOfRunningActions then
            runningActionNum = actionManager:getNumberOfRunningActions()
        end
        formatStr = formatStr.." RunningActions:"..runningActionNum

        local playerCount = FieldManager:getMapCharactersNum()
        local npcCount = table.nums(NpcManager:getNpcVec())
        formatStr = formatStr.."\n Players:"..playerCount.." Npcs:"..npcCount
        label:setString(formatStr)
    end
end

-- 刷新重加载界面
function GameApp:refreshReloadView(pg)
    if pg >= 0 and pg < 1 then
        if not self._reloadView then
            self._reloadView = require("app.ReloadingView").new()
            self:getRunningScene():addChild(self._reloadView, 100000)
        end
        self._reloadView:setProgress(pg)
    elseif pg == -1 then
        self:restartApp()
    else
        if self._reloadView then
            self._reloadView:removeFromParent()
            self._reloadView = nil
        end
    end
end

-- 重启app，暂时支持android
function GameApp:restartApp()
    if cc.RestartApp then
        cc.RestartApp()
    end
end

-- action 1 直接进入角色选择
-- isClean 是否先进入cleanScene
function GameApp:relaunchApp(action, isClean)
    GameApp.super.relaunchApp(self)
    self:refreshReloadView(-2)

    app.session:disconnect(function()
        _RelaunchAppAction = action

        self:_clearApp()

        local withLog = true
        if action and action == 1 then
            withLog = false
            AM.play(Res.LoginMusic,true,AM.TagMusic);
        end

        print("relaunchApp", isClean)
        if isClean then
            if cc.LaunchClearScene then
                cc.LaunchClearScene()
            end
        else
            if cc.RelaunchApp then
                cc.RelaunchApp(withLog)
            end
        end
    end)
end

function GameApp:_clearApp()
    if safeapi then
        safeapi.isLogin = false
    end
    EM:notify("OnRoleDisable")
    app:sendMsg("IMModel", "logout")
    scheduler.cancelAllCron()
    AM.stopAll()
    EM:removeAll()
    self._debugScheduleHandler=nil
end

-- 重启app lua层需要做的一些清理
function GameApp:onRestartApp()
    GameApp.super.relaunchApp(self)
    app.session:disconnect(function()
        self:_clearApp()
    end)
end

-- 聊天中添加自定义命令
function GameApp:addCommand(key, func)
    self._commands = self._commands or {}
    self._commands[key] = func
end

function GameApp:getCommands(key, func)
    return self._commands
end

function GameApp:getRunningScene()
    return Director:getRunningScene()
end

function GameApp:registClass(className,path)
    self._classDict[className] = path
end

function GameApp:getClass(className)
    local path = self._classDict[className]
    if path then
        return require(path)
    end
    return nil
end

function GameApp:setLaunchCtl(ctl)
    self._launchCtl = ctl
end

function GameApp:getLaunchCtl()
    return self._launchCtl
end

-- 注册/获取 实例
function GameApp:registInst(inst)
    self._instDict[inst.__cname] = inst
end
function GameApp:unregistInst(name)
    if self._instDict[name] then
        if self._instDict[name].removeListeners then
            self._instDict[name]:removeListeners()
        end
        self._instDict[name] = nil
    end
end
function GameApp:getInst(name)
    assert(name ~= nil, "GameApp:getInst() name can't be nil")
    if nil == self._instDict[name] then
        release_print("GameApp:getInst() can't find inst by name = ", name)
    end
    return self._instDict[name]
end

function GameApp:sendMsg(name, funcName, ...)
    assert(funcName ~= nil)
    --printf("GameApp:sendMsg name=%s, funcName=%s", name, funcName)
    local inst = self:getInst(name)
    if inst then
        local func = inst[funcName]
        if func then
            return func(inst, ...)
        else
            release_print(string.format("GameApp:sendMsg cant find %s from %s", funcName, name))
        end
    else
        display.pushToast(L("tips_1110010"))
    end
    return nil
end

-- 抛出消息，根据配置 飘字／弹框
function GameApp:popMessage(...)
    UIHandler:showMessage(...)
end

local socketGettime = Socket.gettime
local Director_getUTCTime = cc.Director.getUTCTime

function GameApp:getTime()
--    if Director_getUTCTime then
--        return Director_getUTCTime(Director)
--    else
        return socketGettime()
--    end
end

function GameApp:getUTCTime()
    return Director_getUTCTime(Director)
end

-- 服务器时间
function GameApp:getServerTime(isTimeZone)
    return self.session:getServerTime(isTimeZone)
end

-- 获取周几 1-7, 这是是用服务器时间去判断的
function GameApp:getWeekDay()
    local timeData = self.session:getServerDate()
    local day = timeData.wday --取值1-7，1是周日
    day = day - 1
    if day == 0 then day = 7 end
    return day
end


-- 获取周几 1-7, 这是是用服务器时间去判断的 --过了 凌晨5点才计算
function GameApp:getWeekDayPassDailyUpdate()
    local timeData = self.session:getServerDate()
    local hour = tonumber(GD:getSystemPar("daystartsat", 5))
    local curWDay = timeData.wday
    if timeData.hour < hour then
        -- 小于5点 
        curWDay = curWDay - 1;
        if curWDay == 0 then
            curWDay = 7
        end
    end

    local day = curWDay --取值1-7，1是周日
    day = day - 1
    if day == 0 then day = 7 end
    return day
end


function GameApp:getNextDayTime()
    return self._nextDayTime or 0
end

-- 获取 指定时间  的服务器时间戳 
-- wDay 周一 1 
function GameApp:getServerTimeByHMSW(hour, min, sec, wDay, isNext)
    -- 服务器时区
    local t = math.floor(app.session:getServerTime(true)) or os.time()

    local date = os.date("*t", t)
    date.hour = hour
    date.min = min
    date.sec = sec
	local t2 = os.time(date)
    if wDay then
        local curWDay = date.wday -1
        if curWDay <= 0 then
            curWDay = 7
        end

        local diffDay = wDay - curWDay
        if diffDay == 0 then
            if t2 < t and isNext then
                diffDay = 7
            else
                diffDay = 0
            end
        else 
            if diffDay < 0 and isNext then
                diffDay = diffDay + 7
            end            
        end
        t2 = t2 + diffDay * 24 * 3600
	else
		if t2 < t and isNext then
			t2 = t2 + 24 * 3600
		end
    end
  
  
	return t2
end


-- 每天凌晨5点（延迟5秒）发送onDailyUpdate事件，并且继续创建下一天的任务
function GameApp:startDailyCron()
    if self._cronHandle then
        scheduler.cancelCron(self._cronHandle)
    end

    if not self._cronTab then
        local hour = tonumber(GD:getSystemPar("daystartsat", 5))
        local cronStr = table.concat({5,0,hour,"*","*","*"}, " ")
        self._cronTab = Crontab.new(cronStr)
    end

    local serverTime = self.session:getServerTime(true)
    local nextTime, duration = self._cronTab:next(serverTime)
    self._nextDayTime = nextTime

    self._cronHandle = scheduler.newCron(function()
            -- 判断是否跨周了, 默认是周一算跨周，可以读取配置
            local weekDay = self.session:getServerWeekDate()
            local isWeekChange = tonumber(weekDay) == 1

            UD:onDailyUpdate(isWeekChange)

            self:startDailyCron()

            EM:notify("onDailyUpdate", isWeekChange)
        end, duration)
end

function GameApp:getStorePath()
    if cc.GetStorePath then
        return cc.GetStorePath()
    end
    local path = FileUtils:getWritablePath().."com.emagroup.hope/"
    return path
end

function GameApp:getPackageName()
    if cc.GetPackageName then
        return cc.GetPackageName()
    end
    return "com.emagroup.hope"
end

function GameApp:dispatchCustomEvent(eventName,userData)
    local event = cc.EventCustom:new(eventName)
    if userData ~= nil then
        event._usedata = userData
    end

    EventDispatcher:dispatchEvent(event)
end

function GameApp:addCustomEventListener(eventName,callback,lNode)
    lNode = lNode or self:getRunningScene()
    local listener = cc.EventListenerCustom:create(eventName,callback)
    EventDispatcher:addEventListenerWithSceneGraphPriority(listener, lNode)
end

-- 获取配置的channelTag，用于登陆传给服务器
function GameApp:getChannelTag()
    local ChannelTag = self.configuration:getValue("ChannelTag")
    if type(ChannelTag)~="string" then
        ChannelTag = "un-know"
    end
    return ChannelTag
end

function GameApp:isInnerBuild()
    return self:getPublisher() == Enums.Publisher.default
end

function GameApp:getPublisher()
    local publisher = self.configuration:getValue("Publisher")
    if type(publisher) ~= "string" then
        publisher = Enums.Publisher.default
    end
    return publisher
end

-- 游戏内逻辑的publisher
function GameApp:getInnerPublisher()
    if self:isInnerBuild() then
        return INNER_PUBLISHER or self:getPublisher()
    else
        return self:getPublisher()
    end
end

function GameApp:isInnerTencent()
    return self:getInnerPublisher() == Enums.Publisher.tencent
end

function GameApp:isInnerEma()
    return self:getInnerPublisher() == Enums.Publisher.ema
end

function GameApp:isInnerPwrd()
    return self:getInnerPublisher() == Enums.Publisher.pwrd
end

function GameApp:isTencent()
    return self:getPublisher() == Enums.Publisher.tencent
end

function GameApp:isEma()
    return self:getPublisher() == Enums.Publisher.ema
end

function GameApp:isPwrd()
    return self:getPublisher() == Enums.Publisher.pwrd
end

function GameApp:getLogAPIIds()
    if not self._logApiIds then
        local conf = self.configuration:getValue("LogAPIIDs")
        if conf then
            self._logApiIds = table.tonumber(string.split(conf, "|"))
        else
            self._logApiIds = {}
        end
    end
    return self._logApiIds
end

function GameApp:getSplusAppId()
    local id = self.configuration:getValue("SplusAPPID")
    return id or ""
end

-- 这里有两个服务器，一个是自己架的屏蔽字服务，一个是腾讯safeSdk提供的
-- 只在腾讯safeSdk不生效的时候，才使用自己架的屏蔽字服务
function GameApp:isNeedCheckInput()
    -- 自己架的屏蔽字服务器不需要了，屏蔽字完全服务器来做
    do return false end

    if SAFE_FILTER_ENABLE then return false end
    if DEBUG > 0 then return false end

    if not self._checkHost then
        local host = self.configuration:getValue("CheckTextHost")
        if host and host ~= "" then
            if string_sub(host, -1) ~= "/" then
                host = host .. "/"
            end
            self._checkHost = host
        end
    end

    return self._checkHost ~= nil
end

-- 废弃 自己架的屏蔽字服务器不需要了，屏蔽字完全服务器来做
--检查玩家输入是否合法
--params
--input string 检查内容
--cb function 回调方法
--isFilter bool true（敏感字替换成"＊"之后返回），false（只返回是否合法）
--showLoading bool 是否显示loading
function GameApp:checkInput(input, cb, isFilter, showLoading)
    -- print("GameApp.checkInput input = " .. input)
    isFilter = isFilter or false
    showLoading = showLoading or false

    local ary = {self._checkHost,0,0,0}
    ary[2] = isFilter and "filter" or "validate"
    ary[3] = "?message="
    ary[4] = string_urlencode(input)

    self.http.reqJsonData(table_concat(ary),function(response,errorMsg,code)
        if cb then
            cb(response,errorMsg,code)
        end
    end,"GET",showLoading)
end

function GameApp:loadResScript(fileName)
    local pos = fileName:find(".lua")
    if pos then
        fileName = fileName:sub(0,pos-1)
    end

    local moduleName = string.gsub(fileName,"/",".")
    local ok,ret = pcall(require,moduleName)
    if not ok then
        release_print("====>GameApp:loadResScript missing ",fileName)
        ret = nil
    end
    return ret
end

function GameApp:getVersion()
    if cc.GetAppVersion then
        return cc.GetAppVersion()
    elseif SharedApplication.getVersion then
        return SharedApplication:getVersion()
    else
        return "1.0.0"
    end
end

function GameApp:getVersionCode()
    if SharedApplication.getVersionCode then
        return SharedApplication:getVersionCode()
    else
        return "1"
    end
end

function GameApp:getResVersion()
    if cc.GetResVersion then
        return cc.GetResVersion()
    else
        return self.configuration:getValue("ResourceVersion") or "0"
    end
end

function GameApp:getFullVersionString()
    return string.format("%s.%s", self:getVersion(), self:getResVersion())
end

function GameApp:getLoginCodeUrl()
    return self.configuration:getValue("Login_Code_URL") or "https://jiguang.tencent.com/download.html"
end

function GameApp:getFormatedVersionStr()
    if not self.format_version_string then
        self.format_version_string = string.format("App:%s Res:%s Battle:%d",self:getVersion(),self:getResVersion(), battle.BattleResConfig:getInstance():getBattleVersion())
    end
    return self.format_version_string
end

--==========Store App Values==========

function GameApp:saveValue(key,value)
    if type(value) == "number" then
        UserDefault:setFloatForKey(key,value)
    elseif type(value) == "boolean" then
        UserDefault:setBoolForKey(key,value)
    elseif type(value) == "string" then
        UserDefault:setStringForKey(key,value)
    else
        return
    end
    UserDefault:flush()
end

function GameApp:getValue(key,default)
    if type(default) == "number" then
        return UserDefault:getFloatForKey(key,default)
    elseif type(default) == "boolean" then
        return UserDefault:getBoolForKey(key,default)
    elseif type(default) == "string" then
        return UserDefault:getStringForKey(key,default)
    end
    return nil
end

function GameApp:checkMapRes(mapId)
    if not mapId or type(mapId)~="number" then
        return "mapid 异常" --@ignore
    end

    local mapPath = "scripts.mapconfig.".."map_"..mapId
    local mapConfig = self:loadResScript(mapPath)
    if not mapConfig then
        return "未找到地图脚本.mapid:"..mapId --@ignore
    end

    if not mapConfig.config.mapPath then
        return "未找到地图资源配置"..mapId --@ignore
    end

    if not FileUtils:isFileExist(mapConfig.config.mapPath) then
        return "未找到地图资源文件。mapid:"..mapId --@ignore
    end

    return nil
end

function GameApp:fixClient()
    local storePath = self:getStorePath()

    if cc.PLATFORM_OS_WINDOWS == TargetPlatform then
        storePath = storePath.."/"
    end
    FileUtils:removeDirectory(storePath)
    FileUtils:createDirectory(storePath)

    UserDefault:setIntegerForKey("didFirstExtract",-1)
    UserDefault:setStringForKey("ResourceVersion",SharedApplication:getVersion()..".1."..SharedApplication:getVersionCode())
    UserDefault:flush()

    self:relaunchApp()
end

function GameApp:isMobilePlatform()
    if (cc.PLATFORM_OS_ANDROID == TargetPlatform) then
        return true
    elseif (cc.PLATFORM_OS_IPHONE == TargetPlatform) or (cc.PLATFORM_OS_IPAD== TargetPlatform) then
        return true
    else
        return false
    end
end

function GameApp:isIOS()
    return (cc.PLATFORM_OS_IPHONE == TargetPlatform) or (cc.PLATFORM_OS_IPAD== TargetPlatform)
end

function GameApp:isAndroid()
    return cc.PLATFORM_OS_ANDROID == TargetPlatform
end

function GameApp:purgeUnusedTextures()
    display.purgeUnUsedArmatureCachedData()
    ActionTimelineCache:purgeUnused()
    SpriteFrameCache:removeUnusedSpriteFrames()
    TextureCache:removeUnusedTextures()
end

function GameApp:purgePolygons()
    if cc.PolygonCache then
        cc.PolygonCache:getInstance():removePolygons()
    end
end


function GameApp:getAnimationInterval()
    return Director:getAnimationInterval()
end

function GameApp:_checkSettingInit()
    if not UserDefault:getBoolForKey(Enums.UserDefaultKey.IsSettingInit, false) then
        local SettingHelp = require("app.utils.SettingHelp")
        local d = SettingHelp.getDefaultSetting()
        UserDefault:setBoolForKey(Enums.UserDefaultKey.IsSettingInit, true)
        UserDefault:setBoolForKey(Enums.UserDefaultKey.FpsFast, d.isFastFps)
        UserDefault:setBoolForKey(Enums.UserDefaultKey.TextureHalfSize, d.isTextureHalfSize)
        UserDefault:setBoolForKey(Enums.UserDefaultKey.EffectAllShow, d.isEffectAllShow)
        UserDefault:flush()
    end
end

function GameApp:isFastFps()
    self:_checkSettingInit()
    local v = UserDefault:getBoolForKey(Enums.UserDefaultKey.FpsFast)
    return v
end

function GameApp:updateFps()
    local fps = self:isFastFps() and FPS_FAST or FPS
    Director:setAnimationInterval(1/fps)
end

function GameApp:isTextureHalfSizeEnable()
    self:_checkSettingInit()
    local v = UserDefault:getBoolForKey(Enums.UserDefaultKey.TextureHalfSize)
    return v
end

function GameApp:initTextureHalfSizeParams()
    if custom.Utils.addTextureHalfFilterRegex then
        custom.Utils:addTextureHalfFilterRegex("res/map/.*")
    end
    
end

function GameApp:updateTextureHalfSize()
    if custom.Utils.setTextureHalfSizeEnable then
        if self:isTextureHalfSizeEnable() then
            cc.Director:getInstance():purgeCachedData()
            custom.Utils:setTextureHalfSizeEnable(true)
        else
            cc.Director:getInstance():purgeCachedData()
            custom.Utils:setTextureHalfSizeEnable(false)
        end
    end
end

function GameApp:isEffectAllShow()
    self:_checkSettingInit()
    local v = UserDefault:getBoolForKey(Enums.UserDefaultKey.EffectAllShow)
    return v
end

function GameApp:getTempFolder()
    if not self._tempFolderPath then
        local ret = ""
        if self:isAndroid() and cc.getExternalPath then
            ret = cc.getExternalPath().."/tmp"
        else
            ret = FileUtils:getWritablePath().."tmp"
        end
        FileUtils:createDirectory(ret)
        self._tempFolderPath = ret
    end
    return self._tempFolderPath
    -- body
end

-- 初始化sdk
function GameApp:initChannel()
    -- self.channelAPI = ChannelAPI.new()
    -- self.channelAPI:init()
    local publisher = app:getPublisher()
    local ChannelClass = nil
  
	if publisher == Enums.Publisher.tencent then --tencent
		ChannelClass = require("app.public.channel.ChannelMsdk")
	elseif publisher == Enums.Publisher.ema then --ema oversea
        ChannelClass = require("app.public.channel.ChannelEmaOversea")
    elseif publisher == Enums.Publisher.pwrd then --pwrd
        ChannelClass = require("app.public.channel.ChannelPwrd")
    else
        ChannelClass = require("app.public.channel.ChannelTest")
    end
    assert(ChannelClass ~= nil, "ChannelClass is nil")
    self.channelAPI = ChannelClass.new()
end


function GameApp:getCurrentLanguage()
    local UserDefault = cc.UserDefault:getInstance()
    local lang = UserDefault:getStringForKey("LANGUAGE","")
    if lang == "" then
        lang =  cc.Configuration:getInstance():getValue('LANGUAGE')
    end
    return lang
end

function GameApp:getMultiLanguageArray()
    local langs = cc.Configuration:getInstance():getValue("MULTI_LANGUAGE")
    if not langs or langs == "" then
        return {}
    end
    local langArr = string.split(langs, ",")
    return langArr
end

function GameApp:isLanguageEN()
    return self:getCurrentLanguage() == "EN"
end

function GameApp:isLanguageCN()
    return self:getCurrentLanguage() == "CHS"
end

function GameApp:changeDefaultLanguage(lang)
    local UserDefault = cc.UserDefault:getInstance()
    UserDefault:setStringForKey("LANGUAGE",lang)
    self:purgeUnusedTextures()
    self:relaunchApp()
end

function GameApp:isMidasPermissionOK()
    return self._midasPermissionOK
end

function GameApp:setMidasPermissionOK(b)
    self._midasPermissionOK = b
    if b then
        app:sendMsg("MallModel", "reqCheckMidasPermission", true)
    end
end

return GameApp
